import { getToken } from "./cache";

// 定义消息体
export class SendMessage {
  type: string;
  to: string;
  data: any;
  constructor(type: string, to: string = "", data: any = null) {
    this.type = type;
    this.to = to;
    this.data = data;
  }
}

export class ReqMessage {
  code: number;
  msg: string;
  data: eMessage;
  constructor(code: number, msg: string = "", data: any = null) {
    this.code = code;
    this.msg = msg;
    this.data = data;
  }
}

export class eMessage {
  type: string;
  data: string;
  fromUid: number;
  fromGroup: number;
  constructor(type: string, data: string = "", fromUid: number = 0, fromGroup: number = 0) {
    this.type = type;
    this.data = data;
    this.fromUid = fromUid;
    this.fromGroup = fromGroup;
  }
}

export class MyWebSocket {
  socketUrl = ""; // 连接 url
  token = ""; // token
  onMessage: (e: any) => void; // 接收到服务器消息回调
  logInfo = true; // 是否打印信息
  connected = false; // 是否连接
  socketTask: UniNamespace.SocketTask | undefined; // webSocket 实例
  heartbeatTimer: number | null = null;
  heartbeatIntervalTime = 1000 * 60; // 心跳间隔
  reconnectTimer: number | null = null;
  reconnectDelayTime = 1000 * 3; // 延迟多久发起重连
  reconnectTimes = 0; // 尝试重连次数
  reconnectMaxTimes = 100; // 最大尝试重连次数
  closeFlag = true; // 是否关闭连接

  constructor(onMessage: (e: any) => void, logInfo = false) {
    if (typeof onMessage != "function") throw Error("onMessage 应该是一个函数");
    this.onMessage = onMessage;
    this.logInfo = logInfo;
  }

  // 初始化
  init(socketUrl: string, token: string = "", type = "init") {
    return new Promise(async (resolve, reject) => {
      try {
        this.socketUrl = socketUrl || this.socketUrl;
        if (!this.socketUrl || typeof this.socketUrl != "string")
          return reject("socketUrl 应该是一个字符串");

        this.token = token;
        if (this.logInfo) {
          console.log(`------ WebSocket 初始化 ------`);
        }
        if (this.connected) return;
        this.socketTask = uni.connectSocket({
          url: this.socketUrl,
          header: {
            "content-type": "application/json",
          },
          protocols: [this.token],
          complete: () => {},
        });

        this.socketTask.onOpen((res) => {
          if (this.logInfo) {
            console.log(`------ WebSocket 连接到服务器, type：${type} ------`, res);
          }
          resolve(res);
          this.connected = true;
          this.closeFlag = false;
          this.reconnectTimes = 0;
          this.heartbeat();
        });

        this.socketTask.onMessage(({ data }) => {
          if (this.logInfo) {
            console.log("------ WebSocket 收到服务器消息 ------", data);
          }
          try {
            if (typeof data == "string") {
              const message = JSON.parse(data);
              if (message.type != "ping" && message.type != "pong") this.onMessage(message);
            }
            if (data instanceof ArrayBuffer) {
              // 处理 ArrayBuffer 类型
            }
          } catch (e) {
            if (this.logInfo) {
              console.log("------ WebSocket 预处理消息错误 ------", e);
            }
          }
        });

        this.socketTask.onError((res) => {
          this.connected = false;
          if (this.logInfo) {
            console.log("------ WebSocket 错误信息 ------", res);
          }
        });

        this.socketTask.onClose(({ code, reason }) => {
          this.connected = false;
          if (this.logInfo) {
            console.log("------ WebSocket 连接关闭 ------", code, reason);
          }
          this.reconnect();
        });
      } catch (e) {
        reject(e);
      }
    });
  }

  // 发送消息
  send(message: SendMessage, verifyFormat = true) {
    if (!this.connected) return Promise.reject("WebSocket 连接未开启");
    if (!(message instanceof SendMessage) && verifyFormat) return Promise.reject("消息格式错误");
    return new Promise((resolve, reject) => {
      if (this.logInfo) {
        console.log("------ WebSocket 发送消息 ------", message);
      }
      this.socketTask?.send({
        data: JSON.stringify(message),
        success: resolve,
        fail: reject,
      });
    });
  }

  // 心跳
  heartbeat() {
    this.send(new SendMessage("ping")).catch(console.log);
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
    }
    this.heartbeatTimer = setInterval(() => {
      if (this.connected) {
        this.send(new SendMessage("ping")).catch(console.log);
      } else {
        this.reconnect();
      }
    }, this.heartbeatIntervalTime);
  }

  // 重连
  reconnect() {
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
    }
    if (this.closeFlag || this.reconnectTimes >= this.reconnectMaxTimes) return;
    this.reconnectTimer = setTimeout(() => {
      if (this.logInfo) {
        console.log("------ WebSocket 尝试重连 ------");
      }
      this.init(this.socketUrl, this.token, "reconnect").catch(console.log);
      this.reconnectTimes++;
    }, this.reconnectDelayTime);
  }

  // 关闭连接
  close(options = {}) {
    this.closeFlag = true;
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
    }
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
    }
    if (!this.connected) return console.error("WebSocket 连接未开启");
    return new Promise((resolve, reject) => {
      this.socketTask?.close({
        ...options,
        success: resolve,
        fail: reject,
        complete: () => {
          this.connected = false;
        },
      });
    });
  }
}

// 创建 webSocket 实例, 调用初始化方法连接到服务器 webSocket.init(socketUrl + token)
export const webSocketX = new MyWebSocket((e: ReqMessage) => {
  if (e.msg != "pong" && e.msg != "ping") {
    uni.$emit("webSocketMessage:" + e.data.type, e.data);
  }
}, false);

export function webSocketInitByToken(token: string) {
  let baseApi = import.meta.env.VITE_APP_WS_ENDPOINT;
  webSocketX.init(baseApi, token);
}

export function webSocketInit() {
  const token = getToken();
  if (token) {
    webSocketInitByToken(token);
  }
}

export function webSocketClose() {
  webSocketX.close();
}
